import { cacheCipher } from '@/settings/encryptionSetting';
import { AesEncryption } from '@/utils/cipher';
import { isNullOrUnDef } from '@/utils/is';

export const createStorage = ({
	prefixKey = '',
	key = cacheCipher.key,
	iv = cacheCipher.iv,
	timeout = null,
	hasEncrypt = true,
} = {}) => {
	if (hasEncrypt && [key.length, iv.length].some((item) => item !== 16)) {
		throw new Error('When hasEncrypt is true, the key or iv must be 16 bits!');
	}

	const encryption = new AesEncryption({ key, iv });

	class Storage {
		constructor() {
			this.prefixKey = prefixKey;
			this.encryption = encryption;
			this.hasEncrypt = hasEncrypt;
		}

		getKey(key) {
			return `${this.prefixKey}${key}`.toUpperCase();
		}

		set(key, value, expire = timeout) {
			try {
				const stringData = JSON.stringify({
					value,
					time: Date.now(),
					expire: !isNullOrUnDef(expire) ? new Date().getTime() + expire * 1000 : null,
				});
				const stringifyValue = this.hasEncrypt ? this.encryption.encryptByAES(stringData) : stringData;
				uni.setStorageSync(this.getKey(key), stringifyValue);
			} catch (err) {
				throw new Error(`setStorageSync error: ${err}`);
			}
		}

		get(key, def = null) {
			const val = uni.getStorageSync(this.getKey(key));
			if (!val) return def;

			try {
				const decVal = this.hasEncrypt ? this.encryption.decryptByAES(val) : val;
				const data = JSON.parse(decVal);
				const { value, expire } = data;
				if (isNullOrUnDef(expire) || expire < new Date().getTime()) {
					this.remove(key);
					return def;
				}
				return value;
			} catch (e) {
				return def;
			}
		}

		remove(key) {
			uni.removeStorageSync(this.getKey(key));
		}

		clear() {
			uni.clearStorageSync();
		}
	}

	return new Storage();
};